package pzl.com.bluetooth.utils;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.content.Intent;
import android.text.TextUtils;
import android.util.Log;

import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import pzl.com.bluetooth.app.CustomApplication;
import pzl.com.bluetooth.bean.BluetoothMessage;
import pzl.com.bluetooth.constant.ConstantUtils;


/**
 * 蓝牙工具类
 *
 * @author zl.peng
 * @version [1.0, 2016-10-17]
 */
public class BluetoothUtils {
    public static final String TAG = "BluetoothUtils";
    private static BluetoothUtils instance;

    public BluetoothAdapter mBluetoothAdapter;
    /**
     * 服务端的uuid
     */
    public UUID uuid;
    /**
     * 服务端监听线程
     */
    private AcceptThread acceptThread;
    /**
     * 客户端和服务端连接的线程
     */
    private ConnectThread connectThread;
    /**
     * 远程socket
     */
    private Map<String, BluetoothSocket> remoteSockets;
    /**
     * 阅读BluetoothMessage的线程map,
     * key是读取的远程蓝牙设备地址，读取的是该地址封装的socket的输入流
     */
    private Map<String, ReadThread> readMessageThreadMap;

    private BluetoothUtils() {
        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        uuid = java.util.UUID.fromString("00001103-0000-1000-8000-00805F9B34FB");
        remoteSockets = new HashMap();
        readMessageThreadMap = new HashMap();
    }

    public synchronized static BluetoothUtils getInstance() {
        if (instance == null) {
            instance = new BluetoothUtils();
        }
        return instance;
    }

    /***
     * 蓝牙设备是否可用
     */
    public boolean isBluetoochDeviceEnable() {
        return mBluetoothAdapter != null;
    }

    /**
     * 服务端监听线程  ---------------------------------------------------------
     */
    public class AcceptThread extends Thread {
        public static final String TAG = "AcceptThread";
        private BluetoothServerSocket mServerSocket;
        private Context mContext;

        public AcceptThread(Context context) {
            mContext = context;
            //因为mServerSocket是final类型的，它必需只能进行仅一次显式初始化，而又可能出异常，没初始化的可能性，使用了中间变量
            try {
                // uuid is the app's UUID string, also used by the client code
                mServerSocket = mBluetoothAdapter.listenUsingRfcommWithServiceRecord("bluetoothServer", uuid);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void run() {
            super.run();

            //一直保持监听，除非发生异常，或连接返回了socket
            while (true) {
                BluetoothSocket socket = null;
                try {
                    socket = mServerSocket.accept();
                    remoteSockets.put(socket.getRemoteDevice().getAddress(), socket);
                    Log.d(TAG, "accept_remoteSockets:" + remoteSockets.toString());
                } catch (IOException e) {
                    e.printStackTrace();
                    //mServerSocket.close();//释放资源，停止监听
                }

                //当accept()返回一个 BluetoothSocket是，该socket已经被连接了，因此你不应该调用connect()（正如你在客户端里所做的）
                // If a connection was accepted
                if (socket != null) {
                    Log.d(TAG, "服务端连接接收(connection was accepted):" + socket.getRemoteDevice().getAddress());
                    Intent intent = new Intent(ConstantUtils.ACTION_RECEIVER_SERVERACCEPT);
                    intent.putExtra("clientAddress", socket.getRemoteDevice().getAddress());
                    CustomApplication.application.sendBroadcast(intent);
                    if (!readMessageThreadMap.containsKey(socket.getRemoteDevice().getAddress())) {
                        ReadThread readThread = new ReadThread(socket.getRemoteDevice().getAddress());
                        readMessageThreadMap.put(socket.getRemoteDevice().getAddress(), readThread);
                        readThread.start();//todo cacel
                    }
                }
                //dataAdapter.addItem(null);
                // dataAdapter.notifyDataSetChanged();
            }
        }

        /**
         * Will cancel the listening socket, and cause the thread to finish
         */
        public void cancel() {
            try {
                mServerSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

    /**
     * 发送数据  -------------------------------------------------------------
     */
    public void sendBluetoothMessageToRemote(BluetoothMessage msg, String remoteDeviceAddress) {
        Log.d(TAG, "param=" + remoteDeviceAddress + "sendMsg_remoteSockets:" + remoteSockets.toString());
        BluetoothSocket bluetoothSocket = remoteSockets.get(remoteDeviceAddress);
        if (bluetoothSocket == null) {
            Log.e(TAG, "remoteSockets 中未找到" + remoteDeviceAddress);
            return;
        }
        OutputStream outputStream = null;
        ObjectOutputStream objectOutputStream = null;
        try {
            msg.receiver = remoteDeviceAddress;
            outputStream = bluetoothSocket.getOutputStream();
            objectOutputStream = new ObjectOutputStream(outputStream);
            objectOutputStream.writeObject(msg);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 客服端连接服务端  ---------------------------------------------------
     */
    public void connect(String remoteAddress) {
        if (TextUtils.isEmpty(remoteAddress)) return;
        if (connectThread != null && connectThread.isAlive()) return;
        connectThread = new ConnectThread(remoteAddress);
        connectThread.start();
    }


    /**
     * client线程，连接线程 ---------------------------------------------------
     *
     * @author zl.peng
     * @version [1.0, 2016-10-13]
     *          bluetoothSocket.connect();为阻塞的
     */
    public class ConnectThread extends Thread {
        static final String TAG = "ConnectThread";
        private final BluetoothSocket mSocket;
        /**
         * 远程设备
         */
        private final BluetoothDevice remoteServerDevice;
        private final String remoteAddress;

        public ConnectThread(String deviceAddress) {
            BluetoothSocket tempSocket = null;
            remoteAddress = deviceAddress;
            remoteServerDevice = mBluetoothAdapter.getRemoteDevice(remoteAddress);
            try {
                tempSocket = remoteServerDevice.createInsecureRfcommSocketToServiceRecord(uuid);
            } catch (IOException e) {
                e.printStackTrace();
            }
            mSocket = tempSocket;
        }

        @Override
        public void run() {
            super.run();
            //如果正在查找设备，那么连接尝试将会很大程度的减缓，并且很有可能会失败
            mBluetoothAdapter.cancelDiscovery();

            try {
                //除非连接成功或发生异常，否则一直阻塞
                Log.d(TAG, "连接成功");
                mSocket.connect();
                remoteSockets.put(remoteAddress, mSocket);
            } catch (IOException e) {
                Log.d(TAG, "连接异常" + e.toString());

                try {
                    mSocket.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
                return;
            }

            // Do work to manage the connection (in a separate thread)
            Log.d(TAG, "连接成功");//todo 发送广播
            Intent intent = new Intent(ConstantUtils.ACTION_RECEIVER_CONNECTEDTOSERVER);
            CustomApplication.application.sendBroadcast(intent);
            if (!readMessageThreadMap.containsKey(mSocket.getRemoteDevice().getAddress())) {
                ReadThread readThread = new ReadThread(mSocket.getRemoteDevice().getAddress());
                readMessageThreadMap.put(mSocket.getRemoteDevice().getAddress(), readThread);
                readThread.start();//todo cacel
            }
        }


        /**
         * Will cancel an in-progress connection, and close the socket
         */
        public void cancel() {
            try {
                mSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

    /**
     * 连接建立后的读取接收数据的线程  ----------------------------------------------
     *
     * @author zl.peng
     * @version [1.0, 2016-10-13]
     */
    public class ReadThread extends Thread {
        public static final String TAG = "ReadThread";

        private BluetoothSocket mSocket;
        private final InputStream mmInStream;

        public ReadThread(String deviceAddress) {
            InputStream tmpIn = null;
            mSocket = remoteSockets.get(deviceAddress);
            try {
                tmpIn = mSocket.getInputStream();
            } catch (IOException e) {
            }
            mmInStream = tmpIn;
        }

        public void run() {
            while (true) {
                try {
                    ObjectInputStream objectInputStream = new ObjectInputStream(mmInStream);
                    BluetoothMessage msg = (BluetoothMessage) objectInputStream.readObject();
                    Log.d(TAG, msg.toString());
                    //发送广播
                    Intent intent = new Intent(ConstantUtils.ACTION_RECEIVER_BLUETOOTHMESSAGE);
                    intent.putExtra("BluetoothMessage", msg);
                    CustomApplication.application.sendBroadcast(intent);
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        /* Call this from the main activity to shutdown the connection */
        public void cancel() {
            try {
                mSocket.close();
            } catch (IOException e) {
            }
        }
    }

    /**
     * 启动服务监听线程,一次
     */
    public void startServiceAcceptThread(Context context) {
        if (acceptThread == null) {
            acceptThread = new AcceptThread(context);
            acceptThread.start();
        }
    }

    public void destroy() {

    }
}
